package com.xique.door.biz.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.xique.common.core.domain.R;
import com.xique.common.core.utils.*;
import com.xique.common.core.web.domain.AjaxResult;
import com.xique.door.bean.dao.BuildingDeviceDAO;
import com.xique.door.bean.dao.estate.EstateUserDeviceListDAO;
import com.xique.door.bean.dao.estate.EstateUserListDAO;
import com.xique.door.bean.domain.*;
import com.xique.door.bean.domain.estate.EstateUser;
import com.xique.door.bean.domain.estate.EstateUserBuilding;
import com.xique.door.bean.domain.estate.EstateUserCard;
import com.xique.door.bean.domain.estate.EstateUserDevice;
import com.xique.door.bean.dto.EstateUserDownDTO;
import com.xique.door.bean.request.estate.EstateUserAddRequest;
import com.xique.door.bean.request.estate.EstateUserEditRequest;
import com.xique.door.bean.request.estate.EstateUserListRequest;
import com.xique.door.bean.search.build.AreaListSearch;
import com.xique.door.bean.search.build.BuildingDeviceListSearch;
import com.xique.door.bean.vo.estate.*;
import com.xique.door.biz.domain.service.IEstateUserDomainService;
import com.xique.door.biz.service.IEstateUserBizService;
import com.xique.door.command.IDeviceEstateUserCommandService;
import com.xique.door.constant.AccessCommandConstant;
import com.xique.door.constant.UserConstant;
import com.xique.door.device.freeview.constant.CommandStatus;
import com.xique.door.service.*;
import com.xique.door.service.estate.IEstateUserBuildingService;
import com.xique.door.service.estate.IEstateUserCardService;
import com.xique.door.service.estate.IEstateUserDeviceService;
import com.xique.door.service.estate.IEstateUserService;
import com.xique.system.api.RemoteSystemUserService;
import com.xique.system.api.dto.OperateDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author shd
 * @create 2022/6/9 13:25
 */
@Service
@Slf4j
public class EstateUserBizServiceImpl implements IEstateUserBizService {

    @Autowired
    private IDCardService cardService;

    @Autowired
    private IBuildingDeviceService buildingDeviceService;

    @Autowired
    private IBuildBuildingService buildingService;

    @Autowired
    private IBuildAreaService areaService;

    @Autowired
    private IEstateUserDomainService estateUserDomainService;

    @Autowired
    private IAccessDeviceService accessDeviceService;

    @Autowired
    private IDeviceEstateUserCommandService deviceEstateUserCommandService;

    @Autowired
    private IEstateUserService estateUserService;

    @Autowired
    private IEstateUserDeviceService estateUserDeviceService;

    @Autowired
    private IDCardDeviceService cardDeviceService;

    @Autowired
    private IEstateUserCardService estateUserCardService;

    @Autowired
    private IEstateUserBuildingService estateUserBuildingService;

    @Autowired
    private RemoteSystemUserService remoteSystemUserService;

    @Override
    public AjaxResult getEstateUserList(EstateUserListRequest request) {

        IPage<EstateUserListDAO> page = estateUserService.page(request);
        List<EstateUserListDAO> daoList = page.getRecords();

        List<Long> userIds = daoList.stream().map(EstateUserListDAO::getId).distinct().collect(Collectors.toList());
        List<EstateUserDeviceListDAO> userDeviceList = estateUserDeviceService.listByUserIds(userIds);
        Map<Long, List<EstateUserDeviceListDAO>> map = new HashMap<>();
        userDeviceList.forEach(item -> {
            List<EstateUserDeviceListDAO> list = map.get(item.getUserId());
            if (CollectionUtil.isEmpty(list)) {
                list = new ArrayList<>();
            }
            list.add(item);
            map.put(item.getUserId(), list);
        });

        List<Long> cardIds = daoList.stream().map(EstateUserListDAO::getCardId).distinct().collect(Collectors.toList());
        Map<Long, List<CardDevice>> cardDeviceMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(cardIds)) {
            CardDevice search = new CardDevice();
            search.setCardIds(cardIds);
            List<CardDevice> cardDeviceList = cardDeviceService.selectDCardDeviceList(search);
            List<Long> deviceIds = cardDeviceList.stream().map(CardDevice::getDeviceId).distinct().collect(Collectors.toList());
            List<AccessDevice> accessDeviceList = accessDeviceService.selectAccessDeviceByIds(deviceIds);
            Map<Long, String> accessDeviceMap = accessDeviceList.stream().collect(Collectors.toMap(AccessDevice::getId, AccessDevice::getName));
            cardDeviceList.forEach(item -> {
                List<CardDevice> list = cardDeviceMap.get(item.getCardId());
                if (CollectionUtil.isEmpty(list)) {
                    list = new ArrayList<>();
                }
                if (StringUtils.isNotBlank(accessDeviceMap.get(item.getDeviceId()))) {
                    item.setDeviceName(accessDeviceMap.get(item.getDeviceId()));
                }
                list.add(item);
                cardDeviceMap.put(item.getCardId(), list);
            });
        }

        List<Long> operateIds = daoList.stream().map(EstateUserListDAO::getOperateId).distinct().collect(Collectors.toList());
        R<List<OperateDTO>> result = remoteSystemUserService.getOperateList(StringUtils.join(operateIds, ","));
        List<OperateDTO> operateList = result.getData();
        Map<Long, String> operateMap = operateList.stream().collect(Collectors.toMap(OperateDTO::getOperateId, OperateDTO::getOperateName));

        List<EstateUserListVO> vos = OrikaUtil.converts(daoList, EstateUserListVO.class);
        vos.forEach(item -> {
            if (StringUtils.isNotNull(operateMap.get(item.getOperateId()))) {
                item.setOperateName(operateMap.get(item.getOperateId()));
            }
            if (CollectionUtil.isNotEmpty(map.get(item.getId()))) {
                List<EstateUserDeviceListVO> userDeviceListVOs = OrikaUtil.converts(map.get(item.getId()), EstateUserDeviceListVO.class);
                Map<String, Object> userDevice = new HashMap<>(3);
                userDevice.put("list", userDeviceListVOs);
                userDevice.put("totalNum", userDeviceListVOs.size());
                userDevice.put("successNum", userDeviceListVOs.stream().filter(v -> AccessCommandConstant.DeviceStatus.SUCCESS.equals(v.getStatus())).count());
                item.setUserDevice(userDevice);
            }
            if (CollectionUtil.isNotEmpty(cardDeviceMap.get(item.getCardId()))) {
                List<EstateUserCardDeviceListVO> userCardDeviceListVOs = OrikaUtil.converts(cardDeviceMap.get(item.getCardId()), EstateUserCardDeviceListVO.class);
                Map<String, Object> cardDevice = new HashMap<>(3);
                cardDevice.put("list", userCardDeviceListVOs);
                cardDevice.put("totalNum", userCardDeviceListVOs.size());
                cardDevice.put("successNum", userCardDeviceListVOs.stream().filter(v -> AccessCommandConstant.DeviceStatus.SUCCESS.equals(v.getStatus())).count());
                item.setCardDevice(cardDevice);
            }
        });

        Map<String, Object> resultMap = new HashMap<>(2);
        resultMap.put("list", vos);
        resultMap.put("total", page.getTotal());

        return AjaxResult.success(resultMap);
    }

    @Override
    public AjaxResult getEstateUserDetail(Long id) {

        EstateUser user = estateUserService.getById(id);

        List<EstateUserCard> userCardList = estateUserCardService.listByUserId(user.getId());
        List<Long> cardIds = userCardList.stream().map(EstateUserCard::getCardId).distinct().collect(Collectors.toList());

        List<EstateUserCardVO> cardVOList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(cardIds)) {
            List<DCard> cardList = cardService.selectDCardByIds(cardIds, UserConstant.Status.NORMAL);
            cardVOList = OrikaUtil.converts(cardList, EstateUserCardVO.class);
        }
        List<EstateUserBuilding> userBuildingList = estateUserBuildingService.listByUserId(user.getId());
        List<Long> buildingIds = userBuildingList.stream().map(EstateUserBuilding::getBuildingId).distinct().collect(Collectors.toList());

        List<EstateUserAreaVO> areaVOList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(buildingIds)) {
            List<BuildBuilding> buildingList = buildingService.selectBuildBuildingListByIds(buildingIds);
            List<Long> areaIds = buildingList.stream().map(BuildBuilding::getAreaId).distinct().collect(Collectors.toList());

            AreaListSearch areaListSearch = new AreaListSearch();
            areaListSearch.setIds(areaIds);
            List<BuildAreaV1> areaList = areaService.selectAreaListV1(areaListSearch);

            for (BuildAreaV1 area : areaList) {
                EstateUserAreaVO areaVO = new EstateUserAreaVO();
                areaVO.setId(area.getId());
                areaVO.setAreaName(area.getAreaName());
                List<EstateUserBuildingVO> buildingVOList = new ArrayList<>();
                for (BuildBuilding building : buildingList) {
                    if (building.getAreaId().equals(area.getId())) {
                        EstateUserBuildingVO buildingVO = new EstateUserBuildingVO();
                        buildingVO.setId(building.getId());
                        buildingVO.setBuildingName(building.getBuildingName());
                        buildingVOList.add(buildingVO);
                    }
                }
                areaVO.setBuildings(buildingVOList);
                areaVOList.add(areaVO);
            }
        }

        EstateUserDetailVO vo = OrikaUtil.convert(user, EstateUserDetailVO.class);
        vo.setCards(cardVOList);
        vo.setAreas(areaVOList);

        return AjaxResult.success(vo);
    }

    /**
     * TODO
     * 1.创建用户信息
     * 2.有卡片则创建卡片信息（判断卡片是否已存在），绑定用户跟卡片信息
     * 3.有楼栋则绑定用户跟楼栋信息
     * 4.绑定用户跟对应楼栋的设备信息
     * 5.下发用户信息到设备
     */
    @Override
    public AjaxResult addEstateUser(EstateUserAddRequest request) {

        EstateUser user = OrikaUtil.convert(request, EstateUser.class);

        DCard card = null;
        if (StringUtils.isNotBlank(request.getCardSn())) {
            List<DCard> cardList = cardService.selectDCardByCardSns(Collections.singletonList(request.getCardSn()), UserConstant.CardStatus.NORMAL);
            if (CollectionUtil.isNotEmpty(cardList)) {
                return AjaxResult.error("该卡片已经被使用，请重新输入卡片");
            }

            card = new DCard();
            card.setParkId(request.getParkId());
            card.setCardSn(request.getCardSn());
            card.setCardType(request.getCardType());
            card.setCustomId(System.currentTimeMillis() + MathUtil.getRandom(20));
        }

        List<EstateUserBuilding> userBuildingList = new ArrayList<>(request.getBuildingIds().size());
        List<EstateUserDevice> userDeviceList = new ArrayList<>();
        List<Long> deviceIds = new ArrayList<>();
        List<CardDevice> cardDeviceList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(request.getBuildingIds())) {
            request.getBuildingIds().forEach(item ->{
                EstateUserBuilding userBuilding = new EstateUserBuilding();
                userBuilding.setBuildingId(item);
                userBuildingList.add(userBuilding);
            });

            BuildingDeviceListSearch search = new BuildingDeviceListSearch();
            search.setBuildingIds(request.getBuildingIds());
            List<BuildingDeviceDAO> buildingDeviceDAOList = buildingDeviceService.selectBuildingDeviceListWithDeviceInfo(search);
            deviceIds = buildingDeviceDAOList.stream().map(BuildingDeviceDAO::getDeviceId).distinct().collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(buildingDeviceDAOList)) {
                buildingDeviceDAOList = buildingDeviceDAOList.stream().collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(
                                Comparator.comparing(BuildingDeviceDAO::getDeviceId))
                        ), ArrayList::new)
                );
            }

            DCard finalCard = card;
            buildingDeviceDAOList.forEach(item -> {
                EstateUserDevice userDevice = new EstateUserDevice();
                userDevice.setParkId(request.getParkId());
                userDevice.setDeviceId(item.getDeviceId());
                userDevice.setDevSn(item.getDevSn());
                userDevice.setModel(item.getModel());
                userDevice.setStatus(AccessCommandConstant.DeviceStatus.SEND);
                if (StringUtils.isBlank(request.getFaceKey())) {
                    userDevice.setStatus(CommandStatus.NOFACE);
                }
                userDevice.setCreateUser(SecurityUtils.getUserId());
                userDevice.setCreateTime(DateUtils.getNowDate());
                userDevice.setUpdateUser(SecurityUtils.getUserId());
                userDevice.setUpdateTime(DateUtils.getNowDate());
                userDeviceList.add(userDevice);

                if (StringUtils.isNotNull(finalCard)) {
                    CardDevice cardDevice = new CardDevice();
                    cardDevice.setParkId(request.getParkId());
                    cardDevice.setCardSn(finalCard.getCardSn());
                    cardDevice.setCustomId(finalCard.getCustomId());
                    cardDevice.setDeviceId(item.getDeviceId());
                    cardDevice.setDevSn(item.getDevSn());
                    cardDevice.setStatus(AccessCommandConstant.DeviceStatus.SEND);
                    cardDevice.setModel(item.getModel());
                    cardDevice.setCreateBy(SecurityUtils.getUserId().toString());
                    cardDevice.setCreateTime(DateUtils.getNowDate());
                    cardDevice.setUpdateBy(SecurityUtils.getUserId().toString());
                    cardDevice.setUpdateTime(DateUtils.getNowDate());
                    cardDeviceList.add(cardDevice);
                }
            });
        }

        AjaxResult result = estateUserDomainService.addEstateUser(user, card, userBuildingList, userDeviceList, cardDeviceList);
        if (AjaxResult.isError(result)) {
            return result;
        }

        if (StringUtils.isNotBlank(request.getFaceKey()) || StringUtils.isNotNull(card)) {
            //TODO 下发用户信息到设备
            List<AccessDevice> deviceList = accessDeviceService.selectAccessDeviceByIds(deviceIds);
            EstateUserDownDTO dto = new EstateUserDownDTO();
            dto.setUserId(user.getId());
            dto.setCustomId(user.getCustomId());
            dto.setUserName(user.getUserName());
            if (StringUtils.isNotBlank(request.getFaceKey())) {
                dto.setFaceUrl(user.getFaceUrl());
            }
            dto.setUserType(user.getUserType());
            if (StringUtils.isNotNull(card)) {
                dto.setCardSn(card.getCardSn());
                dto.setCardType(card.getCardType());
                dto.setCardCustomId(card.getCustomId());
            }
            deviceEstateUserCommandService.downUsers(deviceList, Collections.singletonList(dto));
        }

        return AjaxResult.success();
    }

    @Override
    public AjaxResult editEstateUser(EstateUserEditRequest request) {
        EstateUser oldUser = estateUserService.getById(request.getId());
        EstateUser newUser = OrikaUtil.convert(request, EstateUser.class);
        newUser.setParkId(oldUser.getParkId());
        newUser.setCustomId(oldUser.getCustomId());
        newUser.setUpdateUser(SecurityUtils.getUserId());

        List<EstateUserBuilding> buildingList = estateUserBuildingService.listByUserId(oldUser.getId());
        List<Long> buildingIds = buildingList.stream().map(EstateUserBuilding::getBuildingId).distinct().collect(Collectors.toList());
        if (CollectionUtil.isEmpty(request.getBuildingIds()) && CollectionUtil.isEmpty(buildingIds)) {
            return editEstateUser(newUser, oldUser, request);
        } else {
            return editEstateUserAndDownload(newUser, oldUser, request);
        }
    }

    private AjaxResult editEstateUserAndDownloadWithoutChangeBuildingIds(EstateUser newUser, EstateUser oldUser, EstateUserEditRequest request) {
        boolean faceNeedDownload = false;
        boolean cardNeedDownload = false;
        if (StringUtils.isNotBlank(request.getFaceKey()) && !request.getFaceKey().equals(oldUser.getFaceKey())) {
            faceNeedDownload = true;
        }
        if (StringUtils.isNotBlank(request.getUserType()) && !request.getUserType().equals(oldUser.getUserType())) {
            faceNeedDownload = true;
        }

        BuildingDeviceListSearch search = new BuildingDeviceListSearch();
        search.setBuildingIds(request.getBuildingIds());
        List<BuildingDeviceDAO> buildingDeviceDAOList = buildingDeviceService.selectBuildingDeviceListWithDeviceInfo(search);

        DCard dCard = null;
        if (StringUtils.isNotBlank(request.getCardSn()) && StringUtils.isNotBlank(request.getCardType())) {
            List<EstateUserCard> userCardList = estateUserCardService.listByUserId(request.getId());
            List<Long> cardIds = userCardList.stream().map(EstateUserCard::getCardId).distinct().collect(Collectors.toList());
            List<DCard> cardList = cardService.selectDCardByIds(cardIds, UserConstant.Status.NORMAL);
            if (CollectionUtil.isNotEmpty(cardList)) {
                DCard card = cardList.get(0);
                CardDevice cardDeviceSearch = new CardDevice();
                cardDeviceSearch.setCardId(card.getId());
                List<CardDevice> cardDeviceList = cardDeviceService.selectDCardDeviceList(cardDeviceSearch);
                if (!request.getCardSn().equals(card.getCardSn()) || !request.getCardType().equals(card.getCardType())) {
                    AjaxResult result = updateCard(request, card, oldUser);
                    if (AjaxResult.isError(result)) {
                        return result;
                    }
                    for (CardDevice device : cardDeviceList) {
                        device.setStatus(CommandStatus.SEND);
                        device.setCardSn(request.getCardSn());
                        device.setUpdateTime(new Date());
                        device.setUpdateBy(String.valueOf(SecurityUtils.getUserId()));
                        cardDeviceService.updateDCardDevice(device);
                    }
                    dCard = JSONObject.parseObject(JSONObject.toJSONString(result.get(AjaxResult.DATA_TAG)), DCard.class);
                    cardNeedDownload = true;
                }
            } else {
                // 新增卡片
                AjaxResult result = insertCard(request, oldUser);
                if (AjaxResult.isError(result)) {
                    return result;
                }
                dCard = JSONObject.parseObject(JSONObject.toJSONString(result.get(AjaxResult.DATA_TAG)), DCard.class);
                DCard finalDCard = dCard;
                List<CardDevice> cardDeviceList = new ArrayList<>();
                buildingDeviceDAOList.forEach(item -> {
                    CardDevice cardDevice = new CardDevice();
                    cardDevice.setParkId(newUser.getParkId());
                    cardDevice.setCardSn(finalDCard.getCardSn());
                    cardDevice.setCustomId(finalDCard.getCustomId());
                    cardDevice.setDeviceId(item.getDeviceId());
                    cardDevice.setDevSn(item.getDevSn());
                    cardDevice.setStatus(AccessCommandConstant.DeviceStatus.SEND);
                    cardDevice.setModel(item.getModel());
                    cardDevice.setCreateBy(SecurityUtils.getUserId().toString());
                    cardDevice.setCreateTime(DateUtils.getNowDate());
                    cardDevice.setUpdateBy(SecurityUtils.getUserId().toString());
                    cardDevice.setUpdateTime(DateUtils.getNowDate());
                    cardDeviceList.add(cardDevice);
                });
                cardDeviceService.batchInsertCardDevices(cardDeviceList);
                cardNeedDownload = true;
            }
        }

        List<Long> deviceIds = buildingDeviceDAOList.stream().map(BuildingDeviceDAO::getDeviceId).distinct().collect(Collectors.toList());
        List<AccessDevice> deviceList = accessDeviceService.selectAccessDeviceByIds(deviceIds);
        EstateUserDownDTO dto = new EstateUserDownDTO();
        dto.setUserId(newUser.getId());
        dto.setCustomId(newUser.getCustomId());
        dto.setUserName(newUser.getUserName());
        if (faceNeedDownload) {
            dto.setFaceUrl(newUser.getFaceUrl());
            dto.setUserType(newUser.getUserType());
        }
        if (cardNeedDownload) {
            dto.setCardSn(dCard.getCardSn());
            dto.setCardType(dCard.getCardType());
            dto.setCardCustomId(dCard.getCustomId());
        }
        if (faceNeedDownload || cardNeedDownload) {
            deviceEstateUserCommandService.downUsers(deviceList, Collections.singletonList(dto));
        }
        return AjaxResult.success();
    }

    private AjaxResult editEstateUserAndDownloadWithChangeBuildingIds(EstateUser newUser, EstateUser oldUser, EstateUserEditRequest request,
                                                                      List<Long> estateBuildingIds) {
        //TODO 删除->重发 删除用户跟楼栋的关系 删除用户跟设备的关系 删除卡片跟设备的关系
        // 删除用户跟楼栋的关系
        estateUserBuildingService.removeByIds(estateBuildingIds);

        // 删除用户跟设备的关系
        List<EstateUserDeviceListDAO> deviceList = estateUserDeviceService.listByUserIds(Collections.singletonList(request.getId()));
        List<Long> estateDeviceIds = deviceList.stream().map(EstateUserDeviceListDAO::getId).distinct().collect(Collectors.toList());
        estateUserDeviceService.removeByIds(estateDeviceIds);

        // 删除卡片跟设备的关系
        List<EstateUserCard> userCardList = estateUserCardService.listByUserId(request.getId());
        List<Long> cardIds = userCardList.stream().map(EstateUserCard::getCardId).distinct().collect(Collectors.toList());
        List<DCard> cardList = cardService.selectDCardByIds(cardIds, UserConstant.Status.NORMAL);
        DCard oldCard = null;
        if (CollectionUtil.isNotEmpty(cardList)) {
            DCard card = cardList.get(0);
            oldCard = card;
            CardDevice search = new CardDevice();
            search.setCardId(card.getId());
            List<CardDevice> cardDeviceList = cardDeviceService.selectDCardDeviceList(search);
            List<Long> cardDeviceIds = cardDeviceList.stream().map(CardDevice::getId).distinct().collect(Collectors.toList());
            cardDeviceService.deleteDCardDeviceByIds(cardDeviceIds);
        }

        List<Long> deviceIds = deviceList.stream().map(EstateUserDeviceListDAO::getDeviceId).distinct().collect(Collectors.toList());
        List<AccessDevice> accessDeviceList = accessDeviceService.selectAccessDeviceByIds(deviceIds);
        EstateUserDownDTO dto = new EstateUserDownDTO();
        dto.setUserId(oldUser.getId());
        dto.setCustomId(oldUser.getCustomId());
        dto.setUserName(oldUser.getUserName());
        dto.setFaceUrl(oldUser.getFaceUrl());
        dto.setUserType(oldUser.getUserType());
        if (StringUtils.isNotNull(oldCard)) {
            dto.setCardSn(oldCard.getCardSn());
            dto.setCardType(oldCard.getCardType());
            dto.setCardCustomId(oldCard.getCustomId());
        }
        deviceEstateUserCommandService.deleteUsers(accessDeviceList, Collections.singletonList(dto));

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            return AjaxResult.error();
        }

        DCard dCard = null;
        if (StringUtils.isNotBlank(request.getCardSn()) && StringUtils.isNotBlank(request.getCardType())) {
            if (CollectionUtil.isNotEmpty(cardList)) {
                DCard card = cardList.get(0);
                if (!request.getCardSn().equals(card.getCardSn()) || !request.getCardType().equals(card.getCardType())) {
                    AjaxResult result = updateCard(request, card, oldUser);
                    if (AjaxResult.isError(result)) {
                        return result;
                    }
                    dCard = JSONObject.parseObject(JSONObject.toJSONString(result.get(AjaxResult.DATA_TAG)), DCard.class);
                }
            } else {
                // 新增卡片
                AjaxResult result = insertCard(request, oldUser);
                if (AjaxResult.isError(result)) {
                    return result;
                }
                dCard = JSONObject.parseObject(JSONObject.toJSONString(result.get(AjaxResult.DATA_TAG)), DCard.class);
            }
        }

        List<EstateUserBuilding> userBuildingList = new ArrayList<>(request.getBuildingIds().size());
        List<EstateUserDevice> userDeviceList = new ArrayList<>();
        List<CardDevice> cardDeviceList = new ArrayList<>();
        request.getBuildingIds().forEach(item ->{
            EstateUserBuilding userBuilding = new EstateUserBuilding();
            userBuilding.setBuildingId(item);
            userBuilding.setUserId(newUser.getId());
            userBuildingList.add(userBuilding);
        });

        BuildingDeviceListSearch search = new BuildingDeviceListSearch();
        search.setBuildingIds(request.getBuildingIds());
        List<BuildingDeviceDAO> buildingDeviceDAOList = buildingDeviceService.selectBuildingDeviceListWithDeviceInfo(search);
        if (CollectionUtil.isNotEmpty(buildingDeviceDAOList)) {
            buildingDeviceDAOList = buildingDeviceDAOList.stream().collect(Collectors.collectingAndThen(
                    Collectors.toCollection(() -> new TreeSet<>(
                            Comparator.comparing(BuildingDeviceDAO::getDeviceId))
                    ), ArrayList::new)
            );
        }

        log.info("编辑物业人员，buildingDeviceDAOList：{}", buildingDeviceDAOList);

        DCard finalCard = null == dCard ? oldCard : dCard;
        buildingDeviceDAOList.forEach(item -> {
            if (StringUtils.isNotBlank(newUser.getFaceKey())) {
                EstateUserDevice userDevice = new EstateUserDevice();
                userDevice.setParkId(oldUser.getParkId());
                userDevice.setDeviceId(item.getDeviceId());
                userDevice.setCustomId(newUser.getCustomId());
                userDevice.setUserId(newUser.getId());
                userDevice.setDevSn(item.getDevSn());
                userDevice.setModel(item.getModel());
                userDevice.setStatus(AccessCommandConstant.DeviceStatus.SEND);
                userDevice.setCreateUser(SecurityUtils.getUserId());
                userDevice.setCreateTime(DateUtils.getNowDate());
                userDevice.setUpdateUser(SecurityUtils.getUserId());
                userDevice.setUpdateTime(DateUtils.getNowDate());
                userDeviceList.add(userDevice);
            }

            if (StringUtils.isNotNull(finalCard)) {
                CardDevice cardDevice = new CardDevice();
                cardDevice.setParkId(oldUser.getParkId());
                cardDevice.setCardId(finalCard.getId());
                cardDevice.setCardSn(finalCard.getCardSn());
                cardDevice.setCustomId(finalCard.getCustomId());
                cardDevice.setDeviceId(item.getDeviceId());
                cardDevice.setDevSn(item.getDevSn());
                cardDevice.setStatus(AccessCommandConstant.DeviceStatus.SEND);
                cardDevice.setModel(item.getModel());
                cardDevice.setCreateBy(SecurityUtils.getUserId().toString());
                cardDevice.setCreateTime(DateUtils.getNowDate());
                cardDevice.setUpdateBy(SecurityUtils.getUserId().toString());
                cardDevice.setUpdateTime(DateUtils.getNowDate());
                cardDeviceList.add(cardDevice);
            }
        });

        log.info("编辑物业人员，卡片设备 cardDeviceList：{}", cardDeviceList);

        estateUserBuildingService.saveBatch(userBuildingList);
        estateUserDeviceService.saveBatch(userDeviceList);
        cardDeviceService.batchInsertCardDevices(cardDeviceList);

        if (StringUtils.isNotBlank(newUser.getFaceKey()) || StringUtils.isNotNull(finalCard)) {
            deviceIds = buildingDeviceDAOList.stream().map(BuildingDeviceDAO::getDeviceId).distinct().collect(Collectors.toList());
            accessDeviceList = accessDeviceService.selectAccessDeviceByIds(deviceIds);
            dto = new EstateUserDownDTO();
            dto.setUserId(newUser.getId());
            dto.setCustomId(newUser.getCustomId());
            dto.setUserName(newUser.getUserName());
            if (StringUtils.isNotBlank(newUser.getFaceKey())) {
                dto.setFaceUrl(newUser.getFaceUrl());
            }
            dto.setUserType(newUser.getUserType());
            if (StringUtils.isNotNull(finalCard)) {
                dto.setCardSn(finalCard.getCardSn());
                dto.setCardType(finalCard.getCardType());
                dto.setCardCustomId(finalCard.getCustomId());
            }
            deviceEstateUserCommandService.downUsers(accessDeviceList, Collections.singletonList(dto));
        }

        return AjaxResult.success();
    }

    private AjaxResult editEstateUserAndDownload(EstateUser newUser, EstateUser oldUser, EstateUserEditRequest request) {
        boolean isSuccess = estateUserService.updateById(newUser);
        if (!isSuccess) {
            return AjaxResult.error("更新用户信息失败");
        }
        List<EstateUserBuilding> buildingList = estateUserBuildingService.listByUserId(oldUser.getId());
        List<Long> buildingIds = buildingList.stream().map(EstateUserBuilding::getBuildingId).distinct().collect(Collectors.toList());
        if (ListUtils.isEqualList(buildingIds, request.getBuildingIds())) {
            return editEstateUserAndDownloadWithoutChangeBuildingIds(newUser, oldUser, request);
        } else {
            List<Long> estateBuildingIds = buildingList.stream().map(EstateUserBuilding::getId).distinct().collect(Collectors.toList());
            return editEstateUserAndDownloadWithChangeBuildingIds(newUser, oldUser, request, estateBuildingIds);
        }
    }

    private AjaxResult editEstateUser(EstateUser newUser, EstateUser oldUser, EstateUserEditRequest request) {
        boolean isSuccess = estateUserService.updateById(newUser);
        if (!isSuccess) {
            return AjaxResult.error("更新用户信息失败");
        }
        if (StringUtils.isNotBlank(request.getCardSn()) && StringUtils.isNotBlank(request.getCardType())) {
            List<EstateUserCard> userCardList = estateUserCardService.listByUserId(request.getId());
            List<Long> cardIds = userCardList.stream().map(EstateUserCard::getCardId).distinct().collect(Collectors.toList());
            List<DCard> cardList = cardService.selectDCardByIds(cardIds, UserConstant.Status.NORMAL);
            if (CollectionUtil.isNotEmpty(cardList)) {
                DCard card = cardList.get(0);
                if (!request.getCardSn().equals(card.getCardSn()) || !request.getCardType().equals(card.getCardType())) {
                    AjaxResult result = updateCard(request, card, oldUser);
                    if (AjaxResult.isError(result)) {
                        return result;
                    }
                }
            } else {
                // 新增卡片
                AjaxResult result = insertCard(request, oldUser);
                if (AjaxResult.isError(result)) {
                    return result;
                }
            }
        }
        return AjaxResult.success();
    }

    private AjaxResult updateCard(EstateUserEditRequest request, DCard card, EstateUser oldUser) {
        if (!request.getCardSn().equals(card.getCardSn())) {
            DCard cardSearch = new DCard();
            cardSearch.setParkId(oldUser.getParkId());
            cardSearch.setCardSn(request.getCardSn());
            List<DCard> cardList = cardService.selectCardList(cardSearch);
            if (CollectionUtil.isNotEmpty(cardList)) {
                return AjaxResult.error("该卡片已存在");
            }
        }
        card.setCardSn(request.getCardSn());
        card.setCardType(request.getCardType());
        int count = cardService.updateDCard(card);
        if (count <= 0) {
            return AjaxResult.error("更新卡片失败");
        }
        return AjaxResult.success(card);
    }

    private AjaxResult insertCard(EstateUserEditRequest request, EstateUser oldUser) {
        DCard card = new DCard();
        card.setParkId(oldUser.getParkId());
        card.setCardSn(request.getCardSn());
        card.setCardType(request.getCardType());
        card.setCustomId(System.currentTimeMillis() + MathUtil.getRandom(20));
        int count = cardService.insertDCard(card);
        if (count <= 0) {
            return AjaxResult.error("新增卡片失败");
        }
        return AjaxResult.success(card);
    }

    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public AjaxResult removeEstateUser(Long id) {
        EstateUser user = estateUserService.getById(id);

        // 删除用户
        estateUserService.removeById(id);

        // 删除用户跟楼栋关系
        List<EstateUserBuilding> buildingList = estateUserBuildingService.listByUserId(id);
        List<Long> estateBuildingIds = buildingList.stream().map(EstateUserBuilding::getId).distinct().collect(Collectors.toList());
        estateUserBuildingService.removeByIds(estateBuildingIds);

        // 删除用户跟设备的关系
        List<EstateUserDeviceListDAO> deviceList = estateUserDeviceService.listByUserIds(Collections.singletonList(id));
//        List<Long> estateDeviceIds = deviceList.stream().map(EstateUserDeviceListDAO::getId).distinct().collect(Collectors.toList());
//        estateUserDeviceService.removeByIds(estateDeviceIds);

        // 删除用户跟卡片关系
        List<EstateUserCard> userCardList = estateUserCardService.listByUserId(id);
        List<Long> estateCardIds = userCardList.stream().map(EstateUserCard::getId).distinct().collect(Collectors.toList());
        estateUserCardService.removeByIds(estateCardIds);

        // 删除卡片跟设备的关系
        List<Long> cardIds = userCardList.stream().map(EstateUserCard::getCardId).distinct().collect(Collectors.toList());
        List<DCard> cardList = cardService.selectDCardByIds(cardIds, UserConstant.Status.NORMAL);
        DCard oldCard = null;
        if (CollectionUtil.isNotEmpty(cardList)) {
            DCard card = cardList.get(0);
            oldCard = card;
//            CardDevice search = new CardDevice();
//            search.setCardId(card.getId());
//            List<CardDevice> cardDeviceList = cardDeviceService.selectDCardDeviceList(search);
//            List<Long> cardDeviceIds = cardDeviceList.stream().map(CardDevice::getId).distinct().collect(Collectors.toList());
//            cardDeviceService.deleteDCardDeviceByIds(cardDeviceIds);
        }
        // 删除卡片
        cardService.deleteDCardByIdList(cardIds);

        List<Long> deviceIds = deviceList.stream().map(EstateUserDeviceListDAO::getDeviceId).distinct().collect(Collectors.toList());
        List<AccessDevice> accessDeviceList = accessDeviceService.selectAccessDeviceByIds(deviceIds);
        EstateUserDownDTO dto = new EstateUserDownDTO();
        dto.setUserId(user.getId());
        dto.setCustomId(user.getCustomId());
        dto.setUserName(user.getUserName());
        dto.setFaceUrl(user.getFaceUrl());
        dto.setUserType(user.getUserType());
        if (StringUtils.isNotNull(oldCard)) {
            dto.setCardSn(oldCard.getCardSn());
            dto.setCardType(oldCard.getCardType());
            dto.setCardCustomId(oldCard.getCustomId());
        }
        deviceEstateUserCommandService.deleteUsers(accessDeviceList, Collections.singletonList(dto));

        return AjaxResult.success();
    }

    @Override
    public AjaxResult downEstateUser(Long id) {
        EstateUser user = estateUserService.getById(id);

        List<EstateUserCard> userCardList = estateUserCardService.listByUserId(user.getId());
        List<Long> cardIds = userCardList.stream().map(EstateUserCard::getCardId).distinct().collect(Collectors.toList());
        List<DCard> cardList = cardService.selectDCardByIds(cardIds, UserConstant.Status.NORMAL);
        DCard card = null;
        if (CollectionUtil.isNotEmpty(cardList)) {
            card = cardList.get(0);
        }

        if (StringUtils.isNotBlank(user.getFaceKey()) || StringUtils.isNotNull(card)) {
            List<EstateUserDeviceListDAO> deviceList = estateUserDeviceService.listByUserIds(Collections.singletonList(user.getId()));
            List<Long> deviceIds = deviceList.stream().map(EstateUserDeviceListDAO::getDeviceId).distinct().collect(Collectors.toList());
            List<AccessDevice> accessDeviceList = accessDeviceService.selectAccessDeviceByIds(deviceIds);
            EstateUserDownDTO dto = new EstateUserDownDTO();
            dto.setUserId(user.getId());
            dto.setCustomId(user.getCustomId());
            dto.setUserName(user.getUserName());
            if (StringUtils.isNotBlank(user.getFaceKey())) {
                dto.setFaceUrl(user.getFaceUrl());
            }
            dto.setUserType(user.getUserType());
            if (StringUtils.isNotNull(card)) {
                dto.setCardSn(card.getCardSn());
                dto.setCardType(card.getCardType());
                dto.setCardCustomId(card.getCustomId());
            }
            deviceEstateUserCommandService.downUsers(accessDeviceList, Collections.singletonList(dto));
        }
        return AjaxResult.success();
    }
}
